using AnimCollections;
using AOT;
using System;
using Unity.Burst;
using UnityEngine.Animations;
using UnityEngine.Playables;

namespace DA.AnimGraph {
    [BurstCompile]
    public unsafe partial struct SelectNode : INode {
        public struct SetupData : INodeSetupData {
            public IntLinkDesc stateDesc;
            public float defaultTransitionDuration;
            public TransitionDesc[] transitionsDesc;
            public Unity.Mathematics.int2[] inputMappingDesc; // x:输入数字, y:下标
        }

        //
        private IntLink linkState;
        private float defaultTransitionDuration;
        private TransitionInfo transitionInfo;
        private TransitionEvaluator transitionEvaluator;
        private InputMapping inputMapping;

        //
        private NodeHead head;
        private AnimationMixerPlayable playable;
        private int lastState; // 当前/最新的端口

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.DisposeDelegate))]
        private static bool Dispose(in void* node) {
            var self = (SelectNode*)node;

            self->transitionInfo.Dispose();
            self->transitionEvaluator.Dispose();
            return true;
        }

        private void Setup(in SetupData data, ref GraphBuffer buffer, in NodeLink* link) {
            linkState = new IntLink(data.stateDesc);
            defaultTransitionDuration = data.defaultTransitionDuration;

            transitionInfo = new TransitionInfo(data.transitionsDesc, ref buffer);
            transitionEvaluator = new TransitionEvaluator(5, ref buffer);
            inputMapping = new InputMapping(data.inputMappingDesc, ref buffer);
        }

        private static Playable BuildPlayableGraph(in void* node, in NodeLink* link, in PlayableGraph graph, in BuildContext buildContext) {
            var self = (SelectNode*)node;

            self->head = new NodeHead(link);

            var childrenCount = self->head.childrenCount;
            self->playable = AnimationMixerPlayable.Create(graph, childrenCount);
            for (int i = 0; i < childrenCount; i++) {
                self->playable.ConnectInput(i, self->head.children[i]->BuildPlayableGraph(graph, buildContext), 0);
            }

            return self->playable;
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.EvaluateDelegate))]
        private static void Evaluate(in void* node, bool onEnter, in EvaluateContext evaluateContext, in GraphContext graphContext, ref UpstreamData upstream, ref ScopeContext scope) {
            var self = (SelectNode*)node;

            var childrenCount = self->head.childrenCount;
            float deltaTime = evaluateContext.deltaTime;

            if (onEnter) {
                int state = self->linkState.Evaluate(evaluateContext, graphContext, scope);
                state = self->inputMapping.Check(state);
                if (state >= childrenCount || state < 0) {
                    state = 0; // 如果下标不在合法范围则默认为0
                }
                self->lastState = state;
                self->transitionEvaluator.SetFirstState(state);
            } else {
                int newState = self->linkState.Evaluate(evaluateContext, graphContext, scope);
                newState = self->inputMapping.Check(newState);
                bool needTrans = newState != self->lastState && newState < childrenCount && newState >= 0; // 如果下标不在合法范围则不引起变化
                if (needTrans) {
                    float duration = self->defaultTransitionDuration;
                    if (self->transitionInfo.Check(self->lastState, newState, out Transition transition)) {
                        duration = transition.duration;
                    }
                    bool addSuccess = self->transitionEvaluator.AddNewState(newState, duration);
                    if (addSuccess) {
                        self->lastState = newState;
                    }
                }
            }

            self->transitionEvaluator.Evaluate(deltaTime);
            self->transitionEvaluator.GetEnumerator(out TransitionEvaluator.Iter iter);
            while (iter.MoveNext()) {
                var (port, weight) = iter.current;
                var childScope = new ScopeContext(scope, weight);
                self->head.weights[port] = weight;
                NodeLink.Evaluate(self->head.children[port], evaluateContext, graphContext, ref upstream, ref childScope);
                scope.Fusion(childScope);
            }
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.GetTimeStateDelegate))]
        private static IntPtr GetTimeState(in void* node) {
            var self = (SelectNode*)node;
            // 返回当前决定的状态，无论它权重多少
            var result = self->head.children[self->lastState]->GetTimeState();
            return (IntPtr)result;
        }
    }
}
